#pragma once

#include <math.h>

#include "land_base_conf.h"

#define LAND_PI 3.141592653589f

typedef union {
    struct { LandFloat x, y, z, w; };
    struct { LandFloat r, g, b, a; };
    struct { LandFloat left, top, right, bottom; };
    LandFloat B[4];
}LandVector;

typedef union {
    struct { LandFloat x, y, w; };
    struct { LandFloat width, height, depth; };
}LandVector2D;

typedef union {
    LandFloat B[16];
    struct {
        LandFloat a11, a21, a31, a41,
            a12, a22, a32, a42,
            a13, a23, a33, a43,
            a14, a24, a34, a44;
    } A;
    LandVector V[4];
}LandMatrix;

typedef union {
    LandFloat B[9];
    struct {
        LandFloat a11, a21, a31,
            a12, a22, a32,
            a13, a23, a33;
    } A;
    LandVector2D V[3];
}LandMatrix2D;

static inline LandFloat
landDotVector(LandVector * v1, LandVector * v2) {
    return (v1->x * v2->x)
        + (v1->y * v2->y) + (v1->z * v2->z);
}
static inline LandFloat
landDotVector2D(LandVector2D* v1, LandVector2D* v2) {
    return (v1->x * v2->x) + (v1->y * v2->y);
}

static inline void
landCrossVector(LandVector* res, LandVector* v) {
    LandFloat x = (res->y * v->z) - (res->z * res->y);
    LandFloat y = (res->z * v->x) - (res->x * v->z);
    LandFloat z = res->x * v->y - res->y * v->x;
    res->x = x; res->y = y; res->z = z;
}

static inline LandFloat
landCrossVector2D(LandVector2D* v1, LandVector2D* v2) {
    return (v1->x * v2->y) - (v1->y * v2->x);
}

static inline void
landNormalizeVector(LandVector* res) {
    LandFloat magSq = (res->x * res->x)
        + (res->y * res->y)
        + (res->z * res->z);
    if (magSq > 0.0f) {
        LandFloat i = 1.0f / (LandFloat)sqrt(magSq);
        res->x *= i;
        res->y *= i;
        res->z *= i;
    }
}
static inline void
landNormalizeVector2D(LandVector2D* res) {
    LandFloat magSq = (res->x * res->x) + (res->y * res->y);
    if (magSq > 0.0f) {
        LandFloat i = 1.0f / (LandFloat)sqrt(magSq);
        res->x *= i;
        res->y *= i;
    }
}

static inline LandFloat
landLengthVector(LandVector* v) {
    return (LandFloat)sqrt((v->x * v->x)
        + (v->y * v->y)
        + (v->z * v->z));
}
static inline LandFloat
landLengthVector2D(LandVector2D* v) {
    return (LandFloat)sqrt((v->x * v->x)
        + (v->y * v->y));
}

static inline void
landAddVector(LandVector* res, LandVector* add) {
    res->x += add->x;
    res->y += add->y;
    res->z += add->z;
}
static inline void
landAddVector2D(LandVector2D* res, LandVector2D* add) {
    res->x += add->x;
    res->y += add->y;
}

static inline void
landSubVector(LandVector* res, LandVector* sub) {
    res->x -= sub->x;
    res->y -= sub->y;
    res->z -= sub->z;
}

static inline void
landSubVector2D(LandVector2D* res, LandVector2D* sub) {
    res->x -= sub->x;
    res->y -= sub->y;
}

static inline LandFloat
landDistanceVector(LandVector* v1, LandVector* v2) {
    LandFloat dx = v1->x - v2->x;
    LandFloat dy = v1->y - v2->y;
    LandFloat dz = v1->z - v2->z;
    return (LandFloat)sqrt((dx * dx) + (dy * dy) + (dz * dz));
}
static inline LandFloat
landDistanceVector2D(LandVector2D* v1, LandVector2D* v2) {
    LandFloat dx = v1->x - v2->x;
    LandFloat dy = v1->y - v2->y;
    return (LandFloat)sqrt((dx * dx) + (dy * dy));
}


static inline  void
landIdentityMatrix(LandMatrix* res) {
    memset(res, 0, sizeof(LandMatrix));
    res->A.a11 = res->A.a22 = res->A.a33 = res->A.a44 = 1.0f;
}
static inline  void
landIdentityMatrix2D(LandMatrix2D* res) {
    memset(res, 0, sizeof(LandMatrix2D));
    res->A.a11 = res->A.a22 = res->A.a33 = 1.0f;
}

LAND_BASE_API void
landMulMatrix(LandMatrix* res, LandMatrix* mul);
LAND_BASE_API void
landMulMatrix2D(LandMatrix2D* res, LandMatrix2D* mul);

LAND_BASE_API void
landMoveMatrix(LandMatrix* res, LandVector* v);
LAND_BASE_API void
landMoveMatrix2D(LandMatrix2D* res, LandVector2D* v);

LAND_BASE_API void
landScaleMatrix(LandMatrix* res, LandVector* v);
LAND_BASE_API void
landScaleMatrix2D(LandMatrix2D* res, LandVector2D* v);

LAND_BASE_API void
landRotateMatrix(LandMatrix* res, LandVector* v);
LAND_BASE_API void
landRotateMatrixV(LandMatrix* res, LandVector* v, LandFloat radian);
LAND_BASE_API void
landRotateMatrix2D(LandMatrix2D* res, LandFloat radian);

LAND_BASE_API void
landPerspectiveMatrix(LandMatrix* res,
    LandFloat fov, LandFloat aspect, LandFloat n, LandFloat f);
LAND_BASE_API void
landOrthoMatrix(LandMatrix* res,
    LandVector rect, LandFloat b, LandFloat f);

LAND_BASE_API void
landVectorMulMatrix(LandVector* res, LandMatrix* m);
LAND_BASE_API void
landVectorMulMatrixW(LandVector* res, LandMatrix* m, LandFloat w);
LAND_BASE_API void
landVectorMulMatrix2D(LandVector2D* res, LandMatrix2D* m);
LAND_BASE_API void
landVectorMulMatrix2DW(LandVector* res, LandMatrix* m, LandFloat w);

LAND_BASE_API LandFloat
landRadians(LandFloat degrees);
LAND_BASE_API LandFloat
landDegrees(LandFloat radian);

LAND_BASE_API void
landLookAt(LandMatrix* res,
    LandVector* eye, LandVector* center, LandVector* up);